package com.gylang.web.controller;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.TypeUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.gylang.api.core.domain.AjaxResult;
import com.gylang.common.utils.InvokeUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

/**
 * @author gylang
 * data 2021/6/1
 */
@RestController
@RequestMapping("/test")
public class TestController {

	@Value("${gylang.config.unitTest:false}")
	private boolean open;

	@RequestMapping("/method/{clazzName}/{methodName}")
	public AjaxResult<?> methodInvoke(HttpServletRequest request, @RequestBody String body, @PathVariable String methodName, @PathVariable String clazzName) throws ClassNotFoundException {
		// com.gylang.web.controller.TestController#methodInvoke
		if (!open) {
			return AjaxResult.error("非法请求");
		}
		Class<?> clazz = Class.forName(clazzName);

		Object instance = null;
		instance = InvokeUtil.ignoreException(() -> SpringUtil.getBean(clazz));
		instance = ObjectUtil.defaultIfNull(instance, InvokeUtil.ignoreException(() -> ReflectUtil.newInstance(clazzName)));

		Method methodByName = ReflectUtil.getMethodByName(clazz, methodName);

		// 设置参数
		JSONObject jsonObject = JSON.parseObject(body, Feature.OrderedField);

		// 获取参数
		LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
		String[] parameterNames = localVariableTableParameterNameDiscoverer.getParameterNames(methodByName);
		Object[] param = new Object[methodByName.getParameters().length];
		// 设置参数
		if (ArrayUtil.isEmpty(parameterNames)) {
			setParamByIndex(methodByName, jsonObject, param);
		} else {
			setParamByName(methodByName, jsonObject, parameterNames, param);
		}

		return AjaxResult.success(ReflectUtil.<Object>invoke(instance, methodByName, param));
	}

	private void setParamByName(Method methodByName, JSONObject jsonObject, String[] parameterNames, Object[] param) {
		for (int i = 0; i < parameterNames.length; i++) {
			JSONObject object = jsonObject.getJSONObject(parameterNames[i]);
			Type paramType = TypeUtil.getParamType(methodByName, i);
			Object paramValue = object.toJavaObject(paramType);
			param[i] = paramValue;
		}
	}

	private void setParamByIndex(Method methodByName, JSONObject jsonObject, Object[] param) {
		List<String> keyList = new ArrayList<>(jsonObject.keySet());
		for (int i = 0; i < keyList.size(); i++) {
			JSONObject object = jsonObject.getJSONObject(keyList.get(i));
			Type paramType = TypeUtil.getParamType(methodByName, i);
			Object paramValue = object.toJavaObject(paramType);
			param[i] = paramValue;
		}
	}

}
